/*
 * BaseDAOOracle.java
 *
 * Created on 17 de agosto de 2005, 06:19 PM
 */

package com.psiqueware.alheli.dao.oracle;

import com.psiqueware.alheli.dao.BaseDAO;
import com.psiqueware.alheli.dao.ConnectionException;
import com.psiqueware.alheli.dao.DAOException;
import com.psiqueware.alheli.logging.Logger;
import com.psiqueware.alheli.logging.LoggerFactory;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
import java.sql.*;

/**
 * Clase b&aacute;sica de un dao. Responsabilidades: <ul> <il> Proveer de la conexi&oacute;n al HibernateDAO </il> </ul> Provee de las
 * conexiones.
 * <a href="BaseDAOOracle.java.html"><i>Ver fuente</i> </a> </p>
 *
 * @author <a href="mailto:AngelElectrico@psiqueware.com">Angel El&eacute;ctrico</a>
 * @author Psique
 * @noinspection ClassWithoutNoArgConstructor, ClassWithoutLogger, JDBCExecuteWithNonConstantString, JDBCResourceOpenedButNotSafelyClosed
 */
public abstract class BaseDAOOracle implements OracleDAO, BaseDAO
{

    /**
     * Indica la utilizaci&oacute;n de una conexi&oacute;n directa por thin driver.
     */
    public static final int THIN_DRIVER = 0;

    /**
     * Indica la utilizaci&oacute;n de un datasource para conectarse a la base de datos.
     */
    public static final int DATA_SOURCE = 1;

    protected static final String ERROR_RESULTSET = "Error de lectura al ResultSet";

    protected static final String ERROR_QUERY_MSG = "Error al ejecutar query: ";

    /**
     * @noinspection HardcodedFileSeparator
     */
    protected static final String APP_CONTEXT = "java:comp/env";

    /**
     * Logger de clase para BaseDAOOracle
     */
    private static final Logger log = LoggerFactory.getLogger(BaseDAOOracle.class);

    private static final String ORACLE_URL = "jdbc:oracle:thin:@172.19.4.128:1521:DESA002";

    private static final String ORACLE_USER = "pacc";

    private static final String ORACLE_PASSWORD = "pacc";

    /**
     * Data Source que se va a tomar del contexto.
     *
     * @noinspection HardcodedFileSeparator
     */
    private static final String SOURCE = "jdbc/PCC";

    /**
     * Driver a utilizar.
     */
    private int m_driver = 1;

    /**
     * Constructor b&aacute;sico de la clase. Implica: <ul> <il> Utilizar el thin m_driver. </il> <il> Proporcionar la clase que
     * pretende extender esta clase. </il> </ul>
     *
     * @param childClass no se utiliza, poner <code>null</code>.
     *
     * @deprecated Utilize el logger de su aplicaci&oacute;n.
     */
    public BaseDAOOracle(Class childClass)
    {

    }

    /**
     * Constructor que implica definir un m_driver a utilizar.
     *
     * @param driver a utilizar.
     */
    public BaseDAOOracle(int driver)
    {
        this.m_driver = driver;
    }

    /**
     * Ejecuta un query de actualizacion en la base de datos. Autom&aacute;ticamente realiza el
     * commit correspondiente. No cierra la conexi&oacute;n.
     *
     * @param insertQuery Query de actualizaci&oacute;n de la base de datos.
     * @param connection  bajo la que se pretende utilizar el {@link Insert}.
     *
     * @return int con el n&uacute;mero de regsitros afectados.
     *
     * @throws DAOException si hay un error en el Query.
     * @see this.executeUpdate
     */
    protected int executeInsert(String insertQuery, Connection connection) throws DAOException
    {
        return executeUpdate(insertQuery, connection);
    }

    /**
     * Ejecuta un query de eliminaci&oacute;n en la base de datos. Autom&aacute;ticamente realiza el
     * commit correspondiente. No cierra la conexi&oacute;n.
     *
     * @param deleteQuery Query de eliminaci&oacute;n de la base de datos.
     * @param connection  bajo la que se pretende utilizar el {@link Delete}.
     *
     * @return int con el n&uacute;mero de regsitros afectados.
     *
     * @throws DAOException si hay un error en el Query.
     * @see this.executeUpdate
     */
    protected int executeDelete(String deleteQuery, Connection connection) throws DAOException
    {
        return executeUpdate(deleteQuery, connection);
    }

    /**
     * Ejecuta un query de actualizacion en la base de datos. Autom&aacute;ticamente realiza el
     * commit correspondiente. No cierra la conexi&oacute;n.
     *
     * @param updateQuery Query de actualizaci&oacute;n de la base de datos.
     * @param connection  bajo la que se pretende utilizar el {@link Update}.
     *
     * @return int con el n&uacute;mero de regsitros afectados.
     *
     * @throws DAOException si hay un error en el Query.
     */
    protected int executeUpdate(String updateQuery, Connection connection) throws DAOException
    {
        Statement statement = null;
        int salida = 0;
        log.info(updateQuery);
        log.info(connection);

        try
        {
            statement = connection.createStatement();
            salida = statement.executeUpdate(updateQuery);
            connection.commit();
        }
        catch (SQLException sqle)
        {
            log.error(sqle);
            throw new DAOException("Error al intentar ejecutar un update o un insert.", sqle);
        }
        finally
        {
            closeStatement(statement);
        }
        return salida;
    }

    /**
     * Ejecuta un query y devuelve un dataSet de la base de datos.
     *
     * @param selectQuery query de selecci&oacute;n que nos devolver&aacute; un DataSet.
     * @param connection  bajo la que se pretende utilizar el {@link Query}.
     *
     * @return ResultSet con los datos del query.
     *
     * @throws DAOException si hay un error en el Query.
     */
    protected ResultSet executeQuery(String selectQuery, Connection connection) throws DAOException
    {
        //Logeamos el Query y la conexi&oacute;n
        log.info(selectQuery);
        log.info(connection);

        ResultSet resultSet;
        Statement statement;
        try
        {
            statement = connection.createStatement();
            resultSet = statement.executeQuery(selectQuery);
            connection.commit();
        }
        catch (SQLException sqle)
        {
            log.error(sqle);
            throw new DAOException("Error al ejecutar selectQuery: " + selectQuery, sqle);
        }
        return resultSet;
    }

    /**
     * Devuelve una conexi&oacute;n a la base de datos.
     *
     * @return Connection a la base de datos.
     *
     * @throws ConnectionException
     */
    protected Connection getConnection() throws ConnectionException
    {
        Connection connection;
        switch (m_driver)
        {
            case 0:
                connection = this.getConnectionFromThinDriver();
                break;
            default:
                connection = this.getConnectionFromDataSource();
        }
        return connection;

    }

    /**
     * Cierra la conexi&oacute;n.
     *
     * @param connection a cerrar.
     *
     * @throws ConnectionException en caso de alg&uacute;n problema.
     */
    protected void closeConnection(Connection connection) throws ConnectionException
    {
        try
        {
            connection.close();
        }
        catch (Exception e)
        {
            log.error(e);
            throw new ConnectionException("No se  pudo cerrar la conexi&oacute;n.", e);
        }
    }

    /**
     * Cierra el statement
     *
     * @param sqlStatement a cerrar.
     *
     * @throws DAOException en caso de alg&uacute;n problema.
     */
    protected void closeStatement(Statement sqlStatement) throws DAOException
    {
        try
        {
            sqlStatement.close();
        }
        catch (Exception e)
        {
            log.error(e);
            throw new DAOException("No se  pudo cerrar el statement.", e);
        }
    }

    /**
     * Gets the connection attribute of the BaseDAOOracle object
     *
     * @return The connection value
     *
     * @throws ConnectionException
     * @noinspection JNDIResourceOpenedButNotSafelyClosed
     */
    private Connection getConnectionFromDataSource() throws ConnectionException
    {
        Connection connection;
        Context ctx;
        try
        {
            ctx = new InitialContext();
            Context envCtx = (Context) ctx.lookup(APP_CONTEXT);
            DataSource ds = (DataSource) envCtx.lookup(SOURCE);
            connection = ds.getConnection();
            envCtx.close();
            ctx.close();
        }
        catch (NamingException namExc)
        {
            log.error(namExc);
            throw new ConnectionException("Error al tratar de localizar el Data Source", namExc);
        }
        catch (SQLException sqlExc)
        {
            log.error(sqlExc);
            throw new ConnectionException("Error al tratar de obtener la connecci&oacute;n: " + SOURCE, sqlExc);
        }
        log.info(connection);
        return connection;
    }

    /**
     * Obtiene el m_driver delgado de oracle.
     *
     * @return Connection a la base de datos.
     *
     * @throws ConnectionException en caso de no lograrse la conexi&oacute;n.
     * @noinspection CallToDriverManagerGetConnection
     */
    private Connection getConnectionFromThinDriver() throws ConnectionException
    {
        Connection con;
        try
        {
            Class.forName("oracle.jdbc.driver.OracleDriver");
            con = DriverManager.getConnection(ORACLE_URL, ORACLE_USER, ORACLE_PASSWORD);
        }
        catch (ClassNotFoundException classNoFoExc)
        {
            log.error(classNoFoExc);
            throw new ConnectionException("No se pudo encontrar la clase: oracle.jdbc.driver.OracleDriver");
        }
        catch (SQLException sqlExc)
        {
            log.error(sqlExc);
            throw new ConnectionException("Obtener la conecci&oacute;n a: 172.19.4.117:1521:DESA002");
        }
        return con;
    }

    /**
     * Obtiene la siguiente secuencia de oracle.
     *
     * @param secuencia Description of the Parameter
     *
     * @return La siguiente secuencia.
     *
     * @throws DAOException Description of the Exception
     */
    protected int siguiente(String secuencia) throws DAOException, ConnectionException
    {

        int siguienteSecuencia = 0;
        StringBuilder sqlBuffer = new StringBuilder("SELECT ");
        sqlBuffer.append(secuencia);
        sqlBuffer.append(".NEXTVAL ");
        sqlBuffer.append(" FROM DUAL");

        Connection connection = this.getConnection();
        ResultSet secuenciaRs = this.executeQuery(sqlBuffer.toString(), connection);
        try
        {
            if (secuenciaRs.next())
            {
                siguienteSecuencia = secuenciaRs.getInt(1);
            }
        }
        catch (SQLException sqle)
        {
            log.error(sqle);
            throw new DAOException(ERROR_QUERY_MSG + sqlBuffer.toString(), sqle);
        }
        finally
        {
            this.closeConnection(connection);
        }

        log.debug(secuencia + ": " + siguienteSecuencia);
        return siguienteSecuencia;
    }

}





